﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Slock.Utility
{
    /// <summary>
    /// Defines a basic texture atlas 
    /// </summary>
    public class TextureAtlas
    {
        private Dictionary<int, TextureCoordQuad> uvCache = new Dictionary<int, TextureCoordQuad>();

        /// <summary>
        /// Holds 4 texture coordinates, defining a subimage
        /// </summary>
        public struct TextureCoordQuad
        {
            public readonly float StartU;
            public readonly float StartV;
            public readonly float EndU;
            public readonly float EndV;

            public TextureCoordQuad(float sU, float sV, float eU, float eV)
            {
                StartU = sU;
                StartV = sV;
                EndU = eU;
                EndV = eV;
            }
        }

        private int rows;
        private int columns;
        private Point tileSize;
        private Texture2D texture;

        /// <summary>
        /// Initializes a new instance of TextureAtlas
        /// </summary>
        /// <param name="rowCount"></param>
        /// <param name="columnCount"></param>
        /// <param name="textureAtlas"></param>
        public TextureAtlas(int rowCount, int columnCount, Texture2D textureAtlas)
        {
            this.rows = rowCount;
            this.columns = columnCount;
            this.texture = textureAtlas;

            int width = texture.Width / rowCount;
            int height = texture.Height / columnCount;

            this.tileSize = new Point(width, height);

            GenerateTextureCoordinates();
        }

        /// <summary>
        /// Generates texture coordinates based on the row and column count values
        /// <remarks>Starts with index 0. Begins at the top left and works its way down the texture, then goes over one row and repeats</remarks>
        /// </summary>
        public void GenerateTextureCoordinates()
        {
            ClearUVCoordinates();

            int count = -1;

            // we turn each row position into UV coordinates
            for (int x = 0; x < rows; x++)
            {
                for (int y = 0; y < columns; y++)
                {
                    float startU = ToUVCoordinate(texture.Width, tileSize.X * x);
                    float startV = ToUVCoordinate(texture.Height, tileSize.Y * y);
                    float endU = ToUVCoordinate(texture.Width, tileSize.X * (x + 1));
                    float endV = ToUVCoordinate(texture.Height, tileSize.Y * (y + 1));
                    this.AddTextureCoordinate(count++, new TextureCoordQuad(startU, startV, endU, endV));
                }
            }
        }

        /// <summary>
        /// Adds a texture coordinate to the atlas
        /// </summary>
        /// <param name="value"></param>
        /// <param name="textureCoordinate"></param>
        public void AddTextureCoordinate(int value, TextureCoordQuad textureCoordinate)
        { this.uvCache.Add(value, textureCoordinate); }

        /// <summary>
        /// Clears the UV coordinates from the atlas
        /// </summary>
        public void ClearUVCoordinates()
        { uvCache.Clear(); }

        /// <summary>
        /// Gets a TextureCoordQuad from the atlas
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public TextureCoordQuad this[int index]
        { get { return this.uvCache[index]; } }

        /// <summary>
        /// Normalizes a value down to a UV coordinate
        /// </summary>
        /// <param name="size">size of the dimension on the texture(width or height)</param>
        /// <param name="value">the value you wish to normalize down to UV space</param>
        /// <returns>normalized value</returns>
        private float ToUVCoordinate(int size, float value)
        { return value / size; }

        public Texture2D Texture
        { get { return texture; } }

        public Point TileDimension
        { get { return tileSize; } }
        /// <summary>
        /// Get or set the number of columns in the atlas
        /// </summary>
        public int Columns
        { get { return columns; } }
        
        /// <summary>
        /// Get or set the number of rows in the atlas
        /// </summary>
        public int Rows
        { get { return rows; } }
    }
}
